
     Here  is a	tip for	users of 8080 macro assemblers which
I've never seen	in print, but which I've found to be useful.
It  pertains  to the use of the	standard condition codes (Z,
NZ,  C,	 NC,  M,  P,  PO,  PE)	as  parameters	of  a  macro
instruction.
     Let's say you've written a	useful macro to	do something
or  other.  As an example most CP/M programmers	are familiar
with,	let's  consider	 the  CPM  macro.  It  usually	goes
something like this:

CPM	MACRO	FUNC,VALUE
	 IF	 NOT NUL VALUE
	 LXI	 D,VALUE	 ;;Load	D&E with value.
	 ENDIF
	 IF	 NOT NUL FUNC
	 MVI	 C,FUNC		 ;;Load	C reg with Function.
	 ENDIF
	 CALL	 BDOS
	 ENDM

     The  examples  are	written	for the	Digital	Research MAC
assembler,  but	would probably be similar for others. To use
the macro, one might say:

	 CPM	 CON,CR	 ;Send carriage	return to console

Assuming  that	CON had	been EQUated to	2, the CP/M function
for  console  output,  and  CR	had been EQUated to 0DH, the
macro would expand to:

	 LXI	 D,0DH
	 MVI	 C,02H
	 CALL	 0005H

which	would	send  a	 carriage  return  to  the  console.
Similarly,  if	KEY had	been EQUated to	1, the console input
function, the macro call

	 CPM	 KEY

would expand to:

	 MVI	 C,01H
	 CALL	 0005H

which  would  get a keyboard character into register A.	This
macro  is  in fact used	quite frequently by many programmers
who work with CP/M. Note that the IF's prevent unneeded	code
from being assembled where the parameter is not	supplied.
     Now  consider  a  case  where we want to read a console
character only when the	carry flag is set. Perhaps the carry
flag  indicates	 an error condition, and we want the program
to pause. The usual way	of doing this is:

	 ...		 ;Code to set carry on error
	 JNC	 OK	 ;Jump around input routine
	 CPM	 KEY	 ;Get a	character from console.
OK:
	 ...		 ;Continue with	operation.

     To	save programmer	time, we can modify the	CPM macro to
allow a	condition code to be specified as a third parameter.
Then we	could write lines like:

	 CPM	 KEY,,C		 ;Get char if Carry set
	 ...or...
	 PUSH	 PSW		 ;Save output char.
	 MOV	 E,A		 ;Move to E for	output
	 CPM	 CON		 ;Output it
	 POP	 PSW		 ;Get back character
	 CPI	 CR		 ;Was it a carriage return?
	 CPM	 CON,LF,Z	 ;If so, follow	with Line Feed.
	 ...

without	 having	 to  code  the	jump  instructions which are
necessary  to  avoid executing the macro code. We do this by
coding	the  jump instruction into the macro itself. This is
made  easier  by  the  fact  that most macro assemblers	will
allow  us  to  use  the	 value	of  an	opcode	as data. For
compatability  with  Intel  standards,	it  should  be coded
within	parentheses  (required	by  some  assemblers) and if
we're  using  MAC,  should  have spaces	around the opcode to
avoid a	little-known glitch in MAC.
     Let's  work  with an even simpler macro to	see how	this
might  operate.	 We'll	invent	the JUMPIF macro, which	does
nothing	but cause a conditional	jump.

JUMPIF	MACRO	COND,ADDR
	 DB	 ( J&COND )
	 DW	 ADDR
	 ENDM

     In	this simple example, coding the	line:
	 JUMPIF	 NC,EOJ
would expand to:
	 DB	 ( JNC )
	 DW	 EOJ
     "Big deal!", you say. Why not just	code:
	 JNC	 EOJ
     Well,  we want to work the	condition code into a larger
macro,	and  generate  a  jump around the inline code if the
condition  is  false.  Ah, but there's the rub.	In order for
the macro to work properly, the	jump instruction has to	jump
if  the	condition given	is NOT true, and fall through to the
inline	code  if it IS true. To	illustrate, let's invent the
opposite  of  the  JUMPIF  macro, the JMPUNLES (Jump Unless)
macro. We could	do something like this:

JMPUNLES MACRO	COND,ADDR
	 J&COND	 LABL1	 ;Jump around the next instr.
	 JMP	 ADDR	 ;Do the real jump.
LABL1:
	 ENDM

But  this requires the use of two jump instructions where we
know  only  one	 is  required. In practice, of course, LABL1
would be declared LOCAL	or we could only use this macro	once
per program without getting duplicate label errors. There is
a  way	out.  In  all  8080 instructions involving condition
codes,	there  is  a  one bit difference between a condition
code  and  it's	opposite, and it is always the same bit. The
bit  used  is  the  bit	 with  a  value	of 8. Compare the JZ
instruction with the JNZ instruction:

     1	 1   0	 0   1	 0   1	 0     Jump If Zero
     1	 1   0	 0   0	 0   1	 0     Jump If Not Zero
   --- --- --- --- --- --- --- ---
   128	64  32	16  *8*	 4   2	 1     Bit Values

Changing  this	one bit	changes	a JC to	a JNC, a JP to a JM,
and  a	JPO  to	 a JPE.	The bit	has the	same function in the
conditional call and return instructions as well.
     Combining	all  these facts, we can write our CPM macro
with the condition code	parameter. It comes out	looking	like
this:

CPM	MACRO	FUNC,VALUE,COND
	 LOCAL	 LABLX		 ;Generate one-time label
	 IF	 NOT NUL COND	 ;Generate jump	only when needed
	 DB	 ( J&COND ) XOR	8 ;Change condition code to its
				 ;    opposite.
	 DW	 LABLX		 ;Address field	of jump	instr.
	 ENDIF
	 IF	 NOT NUL VALUE
	 LXI	 D,VALUE
	 ENDIF
	 IF	 NOT NUL FUNC
	 MVI	 C,FUNC
	 ENDIF
	 CALL	 BDOS
LABLX:	;Jump here if COND wasn't true.	 Continue...
	 ENDM

     Coding  CPM     CON,LF,NZ	would cause the	opcode to be
expanded as:
	 DB	 ( JNZ ) XOR 8
which is the same as:
	 DB	 ( JZ )
Note  that  the	 COND  parameter  could	have been any of the
valid 8080 condition codes: Z, NZ, C, NC, P, M,	PO, PE.
     Also,  we	could  use  conditional	 calls ( C&COND	) or
conditional  returns  (	R&COND)	and changed the	sense of the
condition  by  XORing  the  opcode  with  8  to	 change	 the
necessary  bit.	This technique can be quite useful in macros
of all types to	allow the use of condition codes on the	same
line   as  the	macro  call,  saving  programmer  time,	 and
lessening  the	chances	of error. For your information,	here
is  a  list  of	 8080  opcodes	which are changed into their
opposites by XORing with 8. Although the ones with condition
codes  will  probably be the most useful, some of the others
may  come in handy some	day. The instruction on	the left has
the "8's" bit as a 0, on the right as a	1.
	 STAX	 LDAX
	 RLC	 RRC
	 RAL	 RAR
	 INX	 DCX
	 SHLD	 LHLD
	 JNC	 JC
	 JNZ	 JZ
	 JP	 JM
	 JPO	 JPE
	 CNC	 CC
	 CNZ	 CZ
	 CP	 CM
	 CPO	 CPE
	 RNC	 RC
	 RNZ	 RZ
	 RP	 RM
	 RPO	 RPE
	 DI	 EI
	 OUT	 IN

Do yourself a favor and	use this trick only where it will
genuinely improve efficiency, not just to make simple code hard
to read.  Happy	Hacking!

		    Gary P. Novosielski
		   (201) 935-4087 Eve's.
